package com.gitee.wsl.collections.set

import com.gitee.wsl.collections.iterator.ChainMutableIterator
import com.gitee.wsl.collections.iterator.EmptyIterator
import com.gitee.wsl.collections.iterator.EmptyMutableIterator

/**
 * Decorates a set of other sets to provide a single unified view.
 *
 *
 * Changes made to this set will actually be made on the decorated set.
 * Add operations require the use of a pluggable strategy.
 * If no strategy is provided then add is unsupported.
 *
 *
 *
 * From version 4.0, this class does not extend
 * [CompositeCollection][org.apache.commons.collections4.collection.CompositeCollection]
 * anymore due to its input restrictions (only accepts Sets).
 * See [COLLECTIONS-424](https://issues.apache.org/jira/browse/COLLECTIONS-424)
 * for more details.
 *
 *
 * @param <E> the type of the elements in this set
 * @since 3.0
</E> */
open class CompositeSet<E> : MutableSet<E> {
    /**
     * Defines callbacks for mutation operations.
     *
     * @param <E> the type of the elements in this instance.
    </E> */
    interface SetMutator<E> {
        /**
         * Called when an object is to be added to the composite.
         *
         * @param composite  the CompositeSet being changed
         * @param sets  all of the Set instances in this CompositeSet
         * @param obj  the object being added
         * @return true if the collection is changed
         * @throws UnsupportedOperationException if add is unsupported
         * @throws ClassCastException if the object cannot be added due to its type
         * @throws NullPointerException if the object cannot be added because its null
         * @throws IllegalArgumentException if the object cannot be added
         */
        fun add(composite: CompositeSet<E>, sets: List<MutableSet<E>>, obj: E): Boolean

        /**
         * Called when a collection is to be added to the composite.
         *
         * @param composite  the CompositeSet being changed
         * @param sets  all of the Set instances in this CompositeSet
         * @param coll  the collection being added
         * @return true if the collection is changed
         * @throws UnsupportedOperationException if add is unsupported
         * @throws ClassCastException if the object cannot be added due to its type
         * @throws NullPointerException if the object cannot be added because its null
         * @throws IllegalArgumentException if the object cannot be added
         */
        fun addAll(
            composite: CompositeSet<E>,
            sets: List<MutableSet<E>>,
            coll: Collection<E>,
        ): Boolean

        /**
         * Called when a Set is added to the CompositeSet and there is a
         * collision between existing and added sets.
         *
         *
         * If `added` and `existing` still have any intersects
         * after this method returns an IllegalArgumentException will be thrown.
         *
         * @param comp  the CompositeSet being modified
         * @param existing  the Set already existing in the composite
         * @param added  the Set being added to the composite
         * @param intersects  the intersection of the existing and added sets
         */
        fun resolveCollision(
            comp: CompositeSet<E>,
            existing: MutableSet<E>,
            added: Set<E>,
            intersects: Collection<E>,
        )
    }

    /** SetMutator to handle changes to the collection  */
    protected lateinit var mutator: SetMutator<E>

    /** Sets in the composite  */
    private val all = ArrayList<MutableSet<E>>()

    /**
     * Creates an empty CompositeSet.
     */
    constructor()

    /**
     * Creates a CompositeSet with just `set` composited.
     *
     * @param set  the initial set in the composite
     */
    constructor(set: MutableSet<E>) {
        addComposited(set)
    }

    /**
     * Creates a composite set with sets as the initial set of composited Sets.
     *
     * @param sets  the initial sets in the composite
     */
    constructor(vararg sets: MutableSet<E>) {
        addComposited(*sets)
    }

    /**
     * Adds an object to the collection, throwing UnsupportedOperationException
     * unless a SetMutator strategy is specified.
     *
     * @param element  the object to add
     * @return `true` if the collection was modified
     * @throws UnsupportedOperationException if SetMutator hasn't been set or add is unsupported
     * @throws ClassCastException if the object cannot be added due to its type
     * @throws NullPointerException if the object cannot be added because its null
     * @throws IllegalArgumentException if the object cannot be added
     */
     override fun add(element: E): Boolean {
        if (mutator == null) {
            throw UnsupportedOperationException("add() is not supported on CompositeSet without a SetMutator strategy")
        }
        return mutator.add(this, all, element)
    }

    /**
     * Adds a collection of elements to this composite, throwing
     * UnsupportedOperationException unless a SetMutator strategy is specified.
     *
     * @param elements  the collection to add
     * @return true if the composite was modified
     * @throws UnsupportedOperationException if SetMutator hasn't been set or add is unsupported
     * @throws ClassCastException if the object cannot be added due to its type
     * @throws NullPointerException if the object cannot be added because its null
     * @throws IllegalArgumentException if the object cannot be added
     */
    override fun addAll(elements: Collection<E>): Boolean {
        if (mutator == null) {
            throw UnsupportedOperationException("addAll() is not supported on CompositeSet without a SetMutator strategy")
        }
        return mutator.addAll(this, all, elements)
    }

    /**
     * Adds a Set to this composite.
     *
     * @param set  the set to add
     * @throws IllegalArgumentException if a SetMutator is set, but fails to resolve a collision
     * @throws UnsupportedOperationException if there is no SetMutator set
     * @throws NullPointerException if `set` is null
     * @see SetMutator
     */
    //@Synchronized
    fun addComposited(set: MutableSet<E>) {

            for (existingSet in this.sets) {
                val intersects: Collection<E> = existingSet.intersect(set)
                if (!intersects.isEmpty()) {
                    if (mutator == null) {
                        throw UnsupportedOperationException("Collision adding composited set with no SetMutator set")
                    }
                    mutator.resolveCollision(this, existingSet, set, intersects)
                    require(existingSet.intersect(set).isEmpty()) { "Attempt to add illegal entry unresolved by SetMutator.resolveCollision()" }
                }
            }
            all.add(set)
    }

    /**
     * Adds these Sets to the list of sets in this composite
     *
     * @param sets  the Sets to be appended to the composite
     */
    fun addComposited(vararg sets: MutableSet<E>) {
        for (set in sets) {
            addComposited(set)
        }

    }

    /**
     * Adds these Sets to the list of sets in this composite.
     *
     * @param set1  the first Set to be appended to the composite
     * @param set2  the second Set to be appended to the composite
     */
    fun addComposited(set1: MutableSet<E>, set2: MutableSet<E>) {
        addComposited(set1)
        addComposited(set2)
    }

    /**
     * Removes all of the elements from this composite set.
     *
     *
     * This implementation calls `clear()` on each set.
     *
     * @throws UnsupportedOperationException if clear is unsupported
     */
     override fun clear() {
        for (coll in all) {
            coll.clear()
        }
    }

    /**
     * Checks whether this composite set contains the object.
     *
     *
     * This implementation calls `contains()` on each set.
     *
     * @param element  the object to search for
     * @return true if obj is contained in any of the contained sets
     */
     override fun contains(element: E): Boolean {
        for (item in all) {
            if (item.contains(element)) {
                return true
            }
        }
        return false
    }

    /**
     * Checks whether this composite contains all the elements in the specified collection.
     *
     *
     * This implementation calls `contains()` for each element in the
     * specified collection.
     *
     * @param elements  the collection to check for
     * @return true if all elements contained
     */
     override fun containsAll(elements: Collection<E>): Boolean {

        for (item in elements) {
            if (!contains(item)) {
                return false
            }
        }
        return true
    }

    /**
     * {@inheritDoc}
     * @see java.util.Set.equals
     */
    override fun equals(other: Any?): Boolean {
        if (other is Set<*>) {
            val set = other
            return set.size == this.size && set.containsAll(this)
        }
        return false
    }



    val sets: List<MutableSet<E>>
        /**
         * Gets the sets being decorated.
         *
         * @return Unmodifiable list of all sets in this composite.
         */
        get() = all

    /**
     * {@inheritDoc}
     * @see java.util.Set.hashCode
     */
    override fun hashCode(): Int {
        var code = 0
        for (e in this) {
            code += e?.hashCode() ?: 0
        }
        return code
    }

    /**
     * Checks whether this composite set is empty.
     *
     *
     * This implementation calls `isEmpty()` on each set.
     *
     * @return true if all of the contained sets are empty
     */
     override fun isEmpty(): Boolean {
        for (item in all) {
            if (!item.isEmpty()) {
                return false
            }
        }
        return true
    }

    /**
     * Gets an iterator over all the sets in this composite.
     *
     *
     * This implementation uses an `IteratorChain`.
     *
     * @return an `IteratorChain` instance which supports
     * `remove()`. Iteration occurs over contained collections in
     * the order they were added, but this behavior should not be relied upon.
     * @see IteratorChain
     */
     override fun iterator(): MutableIterator<E> {
        if (all.isEmpty()) {
            return EmptyMutableIterator
        }
        val chain: ChainMutableIterator<E> = ChainMutableIterator()
        all.forEach({ item -> chain.addChain(item.iterator()) })
        return chain
    }

    /**
     * If a `CollectionMutator` is defined for this CompositeSet then this
     * method will be called anyway.
     *
     * @param element  object to be removed
     * @return true if the object is removed, false otherwise
     */
     override fun remove(element: E): Boolean {
        for (set in this.sets) {
            if (set.contains(element)) {
                return set.remove(element)
            }
        }
        return false
    }

    /**
     * Removes the elements in the specified collection from this composite set.
     *
     *
     * This implementation calls `removeAll` on each collection.
     *
     * @param elements  the collection to remove
     * @return true if the composite was modified
     * @throws UnsupportedOperationException if removeAll is unsupported
     */
     override fun removeAll(elements: Collection<E>): Boolean {
        if (elements.isEmpty()) {
            return false
        }
        var changed = false
        for (item in all) {
            changed = changed or item.removeAll(elements)
        }
        return changed
    }

    /**
     * Removes a set from those being decorated in this composite.
     *
     * @param set  set to be removed
     */
    fun removeComposited(set: Set<E>) {
        all.remove(set)
    }

    /**
     * @since 4.4
     */
//    fun removeIf(filter: Predicate<in E>): Boolean {
//        var changed = false
//        for (item in all) {
//            changed = changed or item.removeIf(filter)
//        }
//        return changed
//    }

    /**
     * Retains all the elements in the specified collection in this composite set,
     * removing all others.
     *
     *
     * This implementation calls `retainAll()` on each collection.
     *
     * @param elements  the collection to remove
     * @return true if the composite was modified
     * @throws UnsupportedOperationException if retainAll is unsupported
     */
     override fun retainAll(elements: Collection<E>): Boolean {
        var changed = false
        for (item in all) {
            changed = changed or item.retainAll(elements)
        }
        return changed
    }


    /**
     * Gets the size of this composite set.
     *
     *
     * This implementation calls `size()` on each set.
     *
     * @return total number of elements in all contained containers
     */
    override val size: Int
        get() {
            var size = 0
            for (item in all) {
                size += item.size
            }
            return size
        }


    /**
     * Returns an array containing all of the elements in this composite.
     *
     * @return an object array of all the elements in the collection
     */
//     override fun toArray(): Array<Any?> {
//        val result = kotlin.arrayOfNulls<Any>(size())
//        var i = 0
//        val it = iterator()
//        while (it.hasNext()) {
//            result[i] = it.next()
//            i++
//        }
//        return result
//    }

    /**
     * Returns an object array, populating the supplied array if possible.
     * See `Collection` interface for full details.
     *
     * @param <T>  the type of the elements in the collection
     * @param array  the array to use, populating if possible
     * @return an array of all the elements in the collection
    </T> */
//     override fun <T> toArray(array: Array<T?>): Array<T?> {
//        val size = size()
//        var result: Array<Any?>? = null
//        if (array.size >= size) {
//            result = array
//        } else {
//            result = kotlin.reflect.jvm.internal.impl.load.kotlin.JvmType.Array.newInstance(
//                array.javaClass.getComponentType(),
//                size
//            ) as Array<Any?>?
//        }
//
//        var offset = 0
//        for (item in all) {
//            for (e in item!!) {
//                result!![offset++] = e
//            }
//        }
//        if (result!!.size > size) {
//            result[size] = null
//        }
//        return result as Array<T?>
//    }

    /**
     * Returns a new Set containing all of the elements.
     *
     * @return A new HashSet containing all of the elements in this composite.
     * The new collection is *not* backed by this composite.
     */
//    fun toSet(): Set<E> {
//        return HashSet(this)
//    }

    companion object {

    }
}
